package org.windowkit.toolkit.widget.table.render;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Polygon;

import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.TableCellRenderer;

import org.ddevil.data.SortOrder;
import org.windowkit.toolkit.widget.table.SortableTableModel;

import com.sun.java.swing.plaf.windows.WindowsLabelUI;

/**
 * This class renders a header for the table with support for drawing arrows on
 * column headers which are sorted.
 */
public class SortTableHeaderRenderer implements TableCellRenderer {
	/**
	 * The original renderer. We will use this for table cells.
	 */
	private final TableCellRenderer theRenderer;

	public SortTableHeaderRenderer(final JTable table) {
		this(table.getTableHeader().getDefaultRenderer());
	}

	/**
	 * Constructor
	 *
	 * @param renderer
	 *            The table renderer.
	 */
	public SortTableHeaderRenderer(final TableCellRenderer renderer) {
		this.theRenderer = renderer;
	}

	/**
	 * Return the table cell renderer.
	 *
	 * @return The table cell renderer we are using.
	 */
	public TableCellRenderer getRenderer() {
		return theRenderer;
	}

	/**
	 * Returns the component which will render the table header.
	 */
	public Component getTableCellRendererComponent(final JTable table, final Object value,
			final boolean isSelected, final boolean hasFocus, final int row, final int column) {
		// Get our component.
		Component component = theRenderer.getTableCellRendererComponent(table,
				value, isSelected, hasFocus, row, column);

		// If the component is a JLabel we know it is part of the header.
		// We replace any new lines with HTML line breaks <BR> and center the
		// text within the label.
		if (component instanceof JLabel) {

            JLabel newLabel = (JLabel) component;

            // Set UI to a windows UI as the MJLabelUI caused problems with
			// centering text.
			newLabel.setUI((WindowsLabelUI) WindowsLabelUI.createUI(newLabel));

            //prepare the label
            newLabel.setHorizontalAlignment(SwingConstants.CENTER);
			newLabel.setVerticalAlignment(SwingConstants.CENTER);
			newLabel.setText("<HTML><CENTER>"
					+ value.toString().replace("\n", "<BR>")
					+ "</CENTER></HTML>");

			//if we have a sort model add the sort info display stuff
			if (table.getModel() instanceof SortableTableModel) {

                SortableTableModel tableModel = (SortableTableModel) table.getModel();
                int modelIndex = table.convertColumnIndexToModel(column);
			    int columnSortDepth = tableModel.getSortDepth(modelIndex);
			    boolean descending = false;

			    if (columnSortDepth != -1) {
			        if (tableModel.getSortDirection(modelIndex) == SortOrder.DESCENDING) {
			            descending = true;
			        }
			        newLabel.setHorizontalTextPosition(SwingConstants.RIGHT);
			        newLabel.setIcon(new HeaderArrowIcon(newLabel.getFont().getSize(),
			                columnSortDepth, descending));
			    } else {
			        //no sort
			        newLabel.setHorizontalTextPosition(SwingConstants.CENTER);
			        newLabel.setIcon(new HeaderBlankIcon(newLabel.getBackground(),
			                newLabel.getFont().getSize()));
			    }

            }
			table.getColumnModel().getColumn(column).setMinWidth(
			        (int) newLabel.getPreferredSize().getWidth());
			newLabel.setMinimumSize(newLabel.getPreferredSize());
			return newLabel;
		}
		return component;
	}

	/**
	 * Draws icons on sorted columns.
	 */
	private static class HeaderArrowIcon implements Icon {
		/**
		 * The size of the font this icon lines up with
		 */
		int size;

		/**
		 * Used in compound sorts to determine where this sort has effect
		 */
		int depth;

		/**
		 * Stores whether the sort represented by this icon is descending.
		 */
		boolean descending;

		/**
		 * Constructor
		 *
		 * @param size
		 *            Size of Icon
		 * @param depth
		 *            If this is a compound sort each arrow will be smaller than
		 *            the last in order to allow the user to easily see the
		 *            order the sorts were applied in
		 * @param descending
		 *            Should this arrow be pointed down or up?
		 */
		HeaderArrowIcon(final int size, final int depth, final boolean descending) {
			this.size = size;
			this.depth = depth;
			this.descending = descending;
		}

		/**
		 * Paints the icon.
		 */
		public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
			Color mainColor = Color.GRAY;
//			Color offSetColor = Color.black;
			Polygon arrow;
			int xPoints[] = new int[3];
			int yPoints[] = new int[3];
			int offsetXPoints[] = new int[3];
			int offsetYPoints[] = new int[3];
//			int offsetX = 4;
//			int offsetY = 4;
			size *= Math.pow(.85, depth);// Make each depth triangle smaller
			if (descending) {
				// Triangle points UP
				// Left Corner
				xPoints[0] = offsetXPoints[0] = x;
				yPoints[0] = offsetYPoints[0] = y + size;

				// Right Corner
				xPoints[1] = offsetXPoints[1] = x + size;
				yPoints[1] = offsetYPoints[1] = y + size;

				// CenterPoint
				xPoints[2] = offsetXPoints[2] = (x + (size / 2));
				yPoints[2] = offsetYPoints[2] = y;

			} else {
				// Triangle points DOWN
				// Left Corner
				xPoints[0] = offsetXPoints[0] = x;
				yPoints[0] = offsetYPoints[0] = y;

				// Right Corner
				xPoints[1] = offsetXPoints[1] = x + size;
				yPoints[1] = offsetYPoints[1] = y;

				// CenterPoint
				xPoints[2] = offsetXPoints[2] = (x + (size / 2));
				yPoints[2] = offsetYPoints[2] = y + size;
			}
			g.setColor(mainColor);
			arrow = new Polygon(xPoints, yPoints, 3);
			g.fillPolygon(arrow);
		}

		public int getIconWidth() {
			return size;
		}

		public int getIconHeight() {
			return size;
		}
	}

	/**
	 * Blank icon for unsorted columns
	 *
	 * @author Eric Lundin
	 *
	 */
	private static class HeaderBlankIcon implements Icon {
		/** Color of header */
		private final Color fillColor;
		/** Size of icon */
		private final int size;

		/**
		 * Constructor.
		 * @param fillColor The header color.
		 * @param size The fon size of the header
		 */
		public HeaderBlankIcon(final Color fillColor, final int size) {
			this.fillColor = fillColor;
			this.size = size;
		}

		/**
		 * {@inheritDoc}
		 */
		public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
			if (fillColor != null) {
				g.setColor(fillColor);
				g.drawRect(x, y, size - 1, size - 1);
			}
		}

		/**
		 * {@inheritDoc}
		 */
		public int getIconWidth() {
			return size;
		}

		/**
		 * {@inheritDoc}
		 */
		public int getIconHeight() {
			return size;
		}

	}

}
